/*=============================================================================
	CommonRender.cpp: Crytek Common render helper functions and structures declarations.
	Copyright (c) 2001 Crytek Studios. All Rights Reserved.

	Revision history:
		* Created by Honich Andrey

=============================================================================*/

#include "StdAfx.h"

// Resource manager internal variables.
ResourceClassMap CBaseResource::m_sResources;
CryCriticalSection CBaseResource::s_cResLock;

CBaseResource& CBaseResource::operator=(const CBaseResource& Src)
{
  return *this;
}

bool CBaseResource::IsValid()
{
  AUTO_LOCK(s_cResLock); // Not thread safe without this

  SResourceContainer *pContainer = GetResourcesForClass(m_ClassName);
  if (!pContainer)
    return false;

  ResourceClassMapItor itRM = m_sResources.find(m_ClassName);

  if (itRM == m_sResources.end())
    return false;
  if (itRM->second != pContainer)
    return false;
  ResourcesMapItor itRL = itRM->second->m_RMap.find(m_NameCRC);
  if (itRL == itRM->second->m_RMap.end())
    return false;
  if (itRL->second != this)
    return false;

  return true;
}

SResourceContainer *CBaseResource::GetResourcesForClass(const CCryNameTSCRC& className)
{
  ResourceClassMapItor itRM = m_sResources.find(className);
  if (itRM == m_sResources.end())
    return NULL;
  return itRM->second;
}

CBaseResource *CBaseResource::GetResource(const CCryNameTSCRC& className, int nID, bool bAddRef)
{
	FUNCTION_PROFILER_RENDER_FLAT
  AUTO_LOCK(s_cResLock); // Not thread safe without this

  SResourceContainer *pRL = GetResourcesForClass(className);
  if (!pRL)
    return NULL;

	int nIndex = RListIndexFromId(nID);

  //assert(pRL->m_RList.size() > nID);
  if (nIndex >= (int)pRL->m_RList.size() || nIndex < 0)
    return NULL;
  CBaseResource *pBR = pRL->m_RList[nIndex];
  if (pBR)
  {
    if (bAddRef)
      pBR->AddRef();
    return pBR;
  }
  return NULL;
}

CBaseResource *CBaseResource::GetResource(const CCryNameTSCRC& className, const CCryNameTSCRC& Name, bool bAddRef)
{
	FUNCTION_PROFILER_RENDER_FLAT
  AUTO_LOCK(s_cResLock); // Not thread safe without this

  SResourceContainer *pRL = GetResourcesForClass(className);
  if (!pRL)
    return NULL;

  CBaseResource *pBR = NULL;
  ResourcesMapItor itRL = pRL->m_RMap.find(Name);
  if (itRL != pRL->m_RMap.end())
  {
    pBR = itRL->second;
    if (bAddRef)
      pBR->AddRef();
    return pBR;
  }
  return NULL;
}

bool CBaseResource::Register(const CCryNameTSCRC& className, const CCryNameTSCRC& Name)
{
  AUTO_LOCK(s_cResLock); // Not thread safe without this

  SResourceContainer *pRL = GetResourcesForClass(className);
  if (!pRL)
  {
    pRL = new SResourceContainer;
    m_sResources.insert(ResourceClassMapItor::value_type(className, pRL));
  }

  assert(pRL);
  if (!pRL)
    return false;

  ResourcesMapItor itRL = pRL->m_RMap.find(Name);
  if (itRL != pRL->m_RMap.end())
    return false;
  pRL->m_RMap.insert(ResourcesMapItor::value_type(Name, this));
  int nIndex;
  if (pRL->m_AvailableIDs.size())
  {
    ResourceIds::iterator it = pRL->m_AvailableIDs.end()-1;
    nIndex = RListIndexFromId(*it);
    pRL->m_AvailableIDs.erase(it);
    assert(nIndex < (int)pRL->m_RList.size());
    pRL->m_RList[nIndex] = this;
  }
  else
  {
    nIndex = pRL->m_RList.size();
    pRL->m_RList.push_back(this);
  }
  m_nID = IdFromRListIndex(nIndex);
  m_NameCRC = Name;
  m_ClassName = className;
  m_nRefCount = 1;

  return true;
}

bool CBaseResource::UnRegister()
{
  AUTO_LOCK(s_cResLock); // Not thread safe without this

  if (IsValid())
  {
    SResourceContainer *pContainer = GetResourcesForClass(m_ClassName);
    assert(pContainer);
    if (pContainer)
    {
      pContainer->m_RMap.erase(m_NameCRC);
      pContainer->m_RList[RListIndexFromId(m_nID)] = NULL;
      pContainer->m_AvailableIDs.push_back(m_nID);
    }
    return true;
  }
  return false;
}

int32 CBaseResource::Release()
{
	IF(m_nRefCount > 0,1)
	{
		int32 nRef = CryInterlockedDecrement(&m_nRefCount);
		if (nRef < 0)
		{
			CryFatalError("CBaseResource::Release() called more than once!");
		}

		if (nRef <= 0)
		{
			UnRegister();
			if (gRenDev && gRenDev->m_pRT)
				gRenDev->m_pRT->RC_ReleaseBaseResource(this);
			return 0;
		}
		return nRef;
	}
	return 0;
}

//=================================================================

SResourceContainer::~SResourceContainer()
{
  for (ResourcesMapItor it=m_RMap.begin(); it!=m_RMap.end();)
  {
    CBaseResource *pRes = it->second;
    if (pRes && CRenderer::CV_r_printmemoryleaks)
      iLog->Log("Warning: ~SResourceContainer: Resource %d was not deleted (%d)", pRes->GetID(), pRes->GetRefCounter());
    ++it; // advance "it" here because the safe release below usually invalidates "it" (calls m_RMap.erase(it))
    SAFE_RELEASE(pRes);
  }
  m_RMap.clear();
  m_RList.clear();
  m_AvailableIDs.clear();
}

void CBaseResource::ShutDown()
{
  if (CRenderer::CV_r_releaseallresourcesonexit)
  {
    ResourceClassMapItor it;
    for (it=m_sResources.begin(); it!=m_sResources.end(); it++)
    {
      SResourceContainer *pCN = it->second;
      SAFE_DELETE(pCN);
    }
    m_sResources.clear();
  }
}

#ifdef MEMREPLAY_WRAP_D3D9

const GUID MemReplayD3DAnnotation::s_guid = { {0xff23dad0},{0xd47b},{0x4d80},{0xaf,0x63,0x7e,0xf5,0xea,0x2c,0x4a,0x9d} };

MemReplayD3DAnnotation::MemReplayD3DAnnotation(IDirect3DResource9* pRes, D3DPOOL pool, size_t sz)
	: m_nRefCount(0)
	, m_pRes(pRes)
	, m_pool(pool)
{
	EMemReplayAllocClass::Class allocClass = m_pool == D3DPOOL_DEFAULT ? EMemReplayAllocClass::C_D3DDefault : EMemReplayAllocClass::C_D3DManaged;
	MEMREPLAY_SCOPE(allocClass, 0);
	MEMREPLAY_SCOPE_ALLOC(pRes, sz, 0);
}

MemReplayD3DAnnotation::~MemReplayD3DAnnotation()
{
	EMemReplayAllocClass::Class allocClass = m_pool == D3DPOOL_DEFAULT ? EMemReplayAllocClass::C_D3DDefault : EMemReplayAllocClass::C_D3DManaged;
	MEMREPLAY_SCOPE(allocClass, 0);
	MEMREPLAY_SCOPE_FREE(m_pRes);
}

HRESULT MemReplayD3DAnnotation::QueryInterface( REFIID riid, void **ppvObject )
{
	if (riid == IID_IUnknown)
	{
		*reinterpret_cast<IUnknown**>(ppvObject) = this;
		return S_OK;
	}

	*ppvObject = NULL;
	return E_NOINTERFACE;
}

ULONG MemReplayD3DAnnotation::AddRef()
{
	return ++ m_nRefCount;
}

ULONG MemReplayD3DAnnotation::Release()
{
	ULONG nr = -- m_nRefCount;

	if (!nr)
		delete this;

	return nr;
}

#endif

#ifdef MEMREPLAY_WRAP_D3D11

const GUID MemReplayD3DAnnotation::s_guid = { {0xff23dad0},{0xd47b},{0x4d80},{0xaf,0x63,0x7e,0xf5,0xea,0x2c,0x4a,0x9d} };

MemReplayD3DAnnotation::MemReplayD3DAnnotation(ID3D11DeviceChild* pRes, size_t sz)
	: m_nRefCount(0)
	, m_pRes(pRes)
{
	MEMREPLAY_SCOPE(EMemReplayAllocClass::C_D3DManaged, 0);
	MEMREPLAY_SCOPE_ALLOC(pRes, sz, 0);
}

MemReplayD3DAnnotation::~MemReplayD3DAnnotation()
{
	MEMREPLAY_SCOPE(EMemReplayAllocClass::C_D3DManaged, 0);
	MEMREPLAY_SCOPE_FREE(m_pRes);
}

HRESULT MemReplayD3DAnnotation::QueryInterface( REFIID riid, void **ppvObject )
{
	if (riid == IID_IUnknown)
	{
		*reinterpret_cast<IUnknown**>(ppvObject) = this;
		return S_OK;
	}

	*ppvObject = NULL;
	return E_NOINTERFACE;
}

ULONG MemReplayD3DAnnotation::AddRef()
{
	return ++ m_nRefCount;
}

ULONG MemReplayD3DAnnotation::Release()
{
	ULONG nr = -- m_nRefCount;

	if (!nr)
		delete this;

	return nr;
}

#endif
